package com.maas.util.mybatis.jpa.littlenb.statement;

import com.littlenb.mybatisjpa.statement.AbstractStatementFactory;
import com.littlenb.mybatisjpa.statement.StatementFactory;
import com.littlenb.mybatisjpa.support.Constant;
import com.littlenb.mybatisjpa.support.template.InsertIgnoreNullSqlTemplate;
import com.littlenb.mybatisjpa.support.template.InsertCertainSqlTemplate;
import com.littlenb.mybatisjpa.support.template.InsertSqlTemplate;
import com.littlenb.mybatisjpa.type.SelectorStrategy;
import com.littlenb.mybatisjpa.util.PersistentUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;

import com.maas.util.mybatis.jpa.littlenb.annotation.mapper.InsertExDefinition;
import com.maas.util.mybatis.jpa.littlenb.common.ex.keygen.SelectKeyGenerator;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * insert  Statement 加强版
 * @author sway.li
 */
public class InsertExStatementFactory extends AbstractStatementFactory {

  public static final StatementFactory INSTANCE = new InsertExStatementFactory();
  MapperBuilderAssistant  builderAssistant =null;

  @Override
  public MappedStatement parseStatement(Configuration configuration, Method method,
      Class<?> targetClass) {
    MappedStatement mappedStatement = null;
    String resourceName = targetClass.toString();

    if (!configuration.isResourceLoaded(resourceName)) {
      configuration.addLoadedResource(resourceName);
    }
    String targetClassName = targetClass.getName();
    Class<?> type = super.recognizeEntityType(method, targetClass);
    LanguageDriver languageDriver = Constant.XML_LANGUAGE_DRIVER;
    SqlSource sqlSource = languageDriver
        .createSqlSource(configuration, "<script> " + parsesql(method, type) + "</script>",
            Object.class);
    String statementId = targetClassName + "." + method.getName();
    MappedStatement.Builder builder = new MappedStatement.Builder(configuration, statementId,
        sqlSource, SqlCommandType.INSERT);
    builder.resource(super.recognizeResource(targetClassName)).lang(languageDriver)
        .statementType(StatementType.PREPARED);

    // keyGenerator
    Field id = PersistentUtil.getIdField(type);
    String keyProperty = id.getName();
    if (SelectorStrategy.CERTAIN.equals(recognizeStrategy(method))) {
      keyProperty = Constant.CERTAINTY_ENTITY_KEY + "." + keyProperty;
    }


    if (id.isAnnotationPresent(GeneratedValue.class)) {
      GeneratedValue generatedValue = id.getAnnotation(GeneratedValue.class);
      if (GenerationType.AUTO.equals(generatedValue.strategy())) {
        /**
         * 自动增长
         */
        builder.keyGenerator(new Jdbc3KeyGenerator()).keyProperty(keyProperty)
            .keyColumn(PersistentUtil.getColumnName(id));
      } else if (GenerationType.IDENTITY.equals(generatedValue.strategy())) {
        /**
         * 使用生成器-如 uuid
         *
         */
        String generator = "".equals(generatedValue.generator()) ? Constant.DEFAULT_KEY_GENERATOR
            : generatedValue.generator();
        KeyGenerator keyGenerator = configuration.getKeyGenerator(generator);
        if (keyGenerator == null) {
          throw new IllegalArgumentException(
              "mybatis mybatisjpa init failure , can not find '" + generator
                  + "' in configuration.");
        }
        builder.keyGenerator(keyGenerator).keyProperty(keyProperty)
            .keyColumn(PersistentUtil.getColumnName(id));
      } else if (GenerationType.SEQUENCE.equals(generatedValue.strategy())) {
        /**
         * 使用序列生成主键
         */
        // 获取selectKey节点对应的selectKeyGenerator的id, 貌似无法获取当前的selectKey
        // 可能需要addMappedStatement后才能获取，算了直接从xml 里的 insert 获取得了
        KeyGenerator keyGenerator = null;
        this.builderAssistant = new MapperBuilderAssistant(configuration, resourceName);

        //selectKey的id, 直接从xml 里的 insert 获取
        String keyStatementId = targetClassName+".insert" + SelectKeyGenerator.SELECT_KEY_SUFFIX;

        //generator 默认值为insert!selectKey
        String generator = "".equals(generatedValue.generator()) ? keyStatementId
                : generatedValue.generator();

        if (configuration.hasKeyGenerator(keyStatementId)) {
          //SQL节点下是否存在<selectKey>节点
          keyGenerator = configuration.getKeyGenerator(generator);

          builder.keyGenerator(keyGenerator).keyProperty(keyProperty)
                  .keyColumn(PersistentUtil.getColumnName(id));
        }

        if (keyGenerator == null) {
          throw new IllegalArgumentException(
                  "mybatis mybatisjpa init failure , can not find '" + generator
                          + "' in configuration."+ targetClassName);
        }

      }
    }
    if(mappedStatement == null){
      mappedStatement = builder.build();
    }
    return mappedStatement;
  }


  private String parsesql(Method method, Class<?> type) {
    InsertExDefinition definition = method.getAnnotation(InsertExDefinition.class);
    SelectorStrategy strategy = recognizeStrategy(method);
    String sql ="";

// 尝试直接写<SELECT_KEY> 无效
//    if(!JpaTools.isEmpty(definition.selectKey())){
//      sql += SelectKeySqlTemplate.INSTANCE.parseSQL(type)+"\n";
//    }

    if (SelectorStrategy.IGNORE_NULL.equals(strategy)) {
      sql += InsertIgnoreNullSqlTemplate.INSTANCE.parseSQL(type);
    }else if (SelectorStrategy.CERTAIN.equals(strategy)) {
      sql += InsertCertainSqlTemplate.INSTANCE.parseSQL(type);
    }else {
      sql += InsertSqlTemplate.INSTANCE.parseSQL(type);
    }
    return sql;
  }

  private SelectorStrategy recognizeStrategy(Method method) {
    InsertExDefinition insertexdefinition = method.getAnnotation(InsertExDefinition.class);
    if (insertexdefinition == null) {
      throw new RuntimeException(
          "'" + method.getName() + "' method is not annotation with 'InsertExDefinition'.");
    }
    return insertexdefinition.strategy();
  }

}
